package cn.ljlao.mall.product.goods.util;



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: liuzhen
 * @Created: 2020-01-09 10:16
 * @Description: Redis 缓存工具类
 */
@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate redisTemplate;

    // ------------------key相关操作------------------

    /**
     * Redis 字符串检查给定 key 是否存在
     *
     * @param key 字符串 Key
     * @return Redis 字符串是否存在指定 key
     */
    public  Boolean hasKey(String key) {
        if (null == key) {
            return false;
        }
        return redisTemplate.hasKey(key);
    }

    /**
     * Redis 字符串删除指定 key
     *
     * @param key 字符串 Key
     * @return Redis 字符串删除指定 key 是否成功
     */
    public Boolean delete(String key) {
        if (null == key) {
            return false;
        }
        return redisTemplate.delete(key);
    }

    /**
     * Redis 字符串批量删除指定 key
     *
     * @param keys 字符串 Key
     * @return Redis 字符串被删除 key 数量
     */
    public Long delete(Collection<String> keys) {
        return redisTemplate.delete(keys);
    }

    /**
     * Redis 字符串设置过期时间
     *
     * @param key      字符串 Key
     * @param timeout  过期时间
     * @param timeUnit 过期时间单位
     * @return Redis 字符串设置指定 Key 过期时间是否成功
     */
    public Boolean expire(String key, long timeout, TimeUnit timeUnit) {
        if (null == key || null == timeUnit) {
            return false;
        }
        return redisTemplate.expire(key, timeout, timeUnit);
    }

    /**
     * Redis 字符串查找所有符合指定模式( pattern) 的key
     *
     * @param pattern 字符串匹配模式
     * @return 所有符合指定模式(pattern)的 key
     */
    public Set<String> keys(String pattern) {
        if (null == pattern) {
            return null;
        }
        return redisTemplate.keys(pattern);
    }

    /**
     * Redis 字符串移除指定 key 的过期时间，指定 key 将持久保持
     *
     * @param key 字符串 key
     * @return 过期时间是否移除成功
     */
    public Boolean persist(String key) {
        if (null == key) {
            return false;
        }
        return redisTemplate.persist(key);
    }

    /**
     * Redis 字符串返回 key 的剩余过期时间
     *
     * @param key      字符串 key
     * @param timeUnit 过期时间单位
     * @return 返回 key 的剩余的过期时间
     */
    public Long getExpire(String key, TimeUnit timeUnit) {
        if (null == key || null == timeUnit) {
            return null;
        }
        return redisTemplate.getExpire(key, timeUnit);
    }

    // ------------------String相关数据类型------------------

    /**
     * Redis 字符串设置指定 key 的值
     *
     * @param key   字符串 key
     * @param value 字符串 Value
     */
    public void set(String key, Object value) {

        if (null == key || null == value) {
            return;
        }
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * Redis 字符串设置 key 的值 并设置过期时间
     *
     * @param key      字符串 key
     * @param value    字符串 Value
     * @param timeout  过期时间
     * @param timeUnit 过期时间单位
     */
    public void set(String key, Object value, long timeout, TimeUnit timeUnit) {

        if (null == key || null == value || null == timeUnit) {
            return;
        }
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * Redis 字符串设置 key 的值并设置过期时间
     * 如果 Key 不存在则新增，存在则不改变已有值。
     *
     * @param key      字符串 Key
     * @param value    字符串 Value
     * @param timeout  过期时间
     * @param timeUnit 时间单位
     * @return 是否成功设置指定 Key 的 Value
     */
    public Boolean setIfAbsent(String key, Object value, long timeout, TimeUnit timeUnit) {

        if (null == key || null == value || null == timeUnit) {
            return null;
        }
        return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
    }

    /**
     * Redis 字符串获取指定 Key 的 Value。如果与该 Key 关联的 Value 不是 string 类型，Redis 将抛出异常，
     * 因为 GET 命令只能用于获取 string Value，如果该 Key 不存在，返回 null
     *
     * @param key 字符串 Key
     * @return Redis 字符串获取指定 Key 的 Value
     */
    public Object get(String key) {

        if (null == key) {
            return null;
        }
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * Redis 字符串先 get 再 set, 即先获取 key 值对应的 value 然后再 set 新的 value 值。
     *
     * @param key   字符串 Key
     * @param value 字符串 Value
     * @return Redis 字符串先 get 再 set Key 的 Value
     */
    public Object getAndSet(String key, Object value) {

        if (null == key) {
            return null;
        }
        return redisTemplate.opsForValue().getAndSet(key, value);
    }

    /**
     * Redis 字符串通过批量 Key 获取批量 Value
     *
     * @param keys 字符串批量 Key
     * @return 获取批量 Key 对应的批量 Value
     */
    public List<Object> multiGet(Collection<String> keys) {

        if (null == keys) {
            return Collections.emptyList();
        }
        return redisTemplate.opsForValue().multiGet(keys);
    }

    /**
     * Redis 字符串将指定 Key 的 Value 原子性的增加 increment。
     * 如果该 Key 不存在，其初始值为0，在 incrby 之后其值为 increment。
     * 如果 Value 的值不能转换为整型值，如Hi，该操作将执行失败并抛出相应异常。操作成功则返回增加后的value值。
     *
     * @param key       字符串 Key
     * @param increment 增量值
     * @return 加上指定的增量值之后 key 的值
     */
    public Long increment(String key, long increment) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * Redis 字符串将指定 Key 的 Value 原子性的减少 decrement。
     * 如果该 Key 不存在，其初始值为0，
     * 在 decrby 之后其值为-decrement。如果 Value 的值不能转换为整型值，
     * 如 Hi，该操作将执行失败并抛出相应的异常。操作成功则返回减少后的 value 值。
     *
     * @param key       字符串 Key
     * @param decrement 减量值
     * @return 减去指定减量值之后 key 的值
     */
    public Long decrement(String key, long decrement) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForValue().decrement(key, decrement);
    }

    /**
     * Redis 字符串如果该 Key 已经存在， APPEND 命令将参数 Value 的数据追加到已存在 Value 的末尾。如果该 Key 不存在，
     * APPEND 命令将会创建一个新的 Key/Value 。返回追加后 Value 的字符串长度。
     *
     * @param key   字符串 Key
     * @param value 追加值
     * @return 追加指定值之后， key 中字符串的长度
     */
    public Integer append(String key, String value) {
        if (key == null) {
            return null;
        }
        return redisTemplate.opsForValue().append(key, value);
    }

    //------------------hash数据类型------------------

    /**
     * Redis 哈希通过 Key 和 Field 获取指定的 Value
     *
     * @param key   哈希 key
     * @param field 哈希表中指定 Field
     * @return 哈希表中指定字段的 Value
     */
    public Object hGet(String key, Object field) {
        if (null == key || null == field) {
            return null;
        }
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * Redis 哈希为指定的 Key 设定 Field/Value 对，如果 Key 不存在，该命令将创建新 Key 以用于存储参数中的 Field/Value 对，
     * 如果参数中的 Field 在该 Key 中已经存在，则用新值覆盖其原有值。
     *
     * @param key   哈希 key
     * @param field 哈希表中指定 Field
     * @param value 哈希表中指定字段的 Value
     */
    public void hPut(String key, Object field, Object value) {
        if (null == key || null == field) {
            return;
        }
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * Redis 哈希判断指定 Key 中的指定 Field 是否存在，返回 true 表示存在，false 表示参数中的 Field 或Key 不存在。
     *
     * @param key   哈希 key
     * @param field 哈希表中指定 Field
     * @return 指定 Key 中的指定 Field 是否存在
     */
    public Boolean hHasKey(String key, Object field) {
        if (null == key || null == field) {
            return false;
        }
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * Redis 哈希指定 Key 的 Hash Value 中删除参数中指定的多个字段，如果不存在的字段将被忽略，
     * 返回实际删除的 Field 数量。如果 Key 不存在，则将其视为空 Hash ，并返回0。
     *
     * @param key    哈希 key
     * @param fields Redis 哈希表字段
     * @return 被成功删除字段的数量，不包括被忽略的字段。
     */
    public Long hDelete(String key, Object... fields) {
        if (null == key || null == fields || fields.length == 0) {
            return 0L;
        }
        return redisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * Redis 哈希通过 key 获取所有的 Field 和 Value
     *
     * @param key 哈希 key
     * @return key 对应的所有 Field 和 Value
     */
    public Map<Object, Object> hEntries(String key) {
        if (key == null) {
            return null;
        }
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * Redis 哈希逐对依次设置参数中给出的 Field/Value 对。如果其中某个 Field 已经存在，则用新值覆盖原有值。
     * 如果 Key 不存在，则创建新 Key，同时设定参数中的 Field/Value。
     *
     * @param key  哈希 key
     * @param hash Redis 哈希键值对
     */
    public void hPutAll(String key, Map<String, Object> hash) {

        if (null == key || null == hash) {
            return;
        }
        redisTemplate.opsForHash().putAll(key, hash);
    }

    /**
     * Redis 哈希获取和参数中指定 Fields 关联的一组 Values ，其返回顺序等同于 Fields 的请求顺序。
     * 如果请求的 Field 不存在，其值对应的 value 为 null 。
     *
     * @param key    哈希 key
     * @param fields Redis 哈希表字段
     * @return 和参数中指定 Fields 关联的一组 Values
     */
    public List<Object> hMultiGet(String key, Collection<Object> fields) {

        if (null == key || null == fields) {
            return null;
        }
        return redisTemplate.opsForHash().multiGet(key, fields);
    }

    /**
     * Redis 哈希对应 key 的字段自增相应的值
     *
     * @param key       哈希 key
     * @param field     哈希表中的字段
     * @param increment 增量值
     * @return 哈希表中字段加上增量后值
     */
    public Long hIncrement(String key, Object field, long increment) {
        if (null == key || null == field) {
            return null;
        }
        return redisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * Redis 哈希通过 key 获取所有 Value
     *
     * @param key 哈希 key
     * @return key 对应所有 Value
     */
    public List hValues(String key) {
        if (key == null) {
            return null;
        }
        return redisTemplate.opsForHash().values(key);
    }

    // ------------------List数据类型------------------

    /**
     * 将一个或多个值插入到 Redis 列表头部。
     * 如果该 Key 不存在，该命令将在插入之前创建一个与该 Key 关联的空 Redis 列表，之后再将数据从 Redis 列表的头部插入。
     * 如果该键的 Value 不是 Redis 列表类型，该命令将将会抛出相关异常。操作成功则返回插入后 Redis 列表中元素的数量
     *
     * @param key    列表 key
     * @param values 插入成员
     * @return 插入值后 Redis 列表长度
     */
    public Long lLeftPushAll(String key, String... values) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForList().leftPushAll(key, values);
    }

    /**
     * 将一个或多个值插入到 Redis 列表尾部。
     * 如果该 Key 不存在，该命令将在插入之前创建一个与该 Key 关联的空 Redis 列表，之后再将数据从 Redis 列表的尾部插入。
     * 如果该键的 Value 不是 Redis 列表类型，该命令将将会抛出相关异常。操作成功则返回插入后 Redis 列表中元素的数量
     *
     * @param key    列表 key
     * @param values 插入成员
     * @return 插入值后 Redis 列表长度
     */
    public Long lRightPushAll(String key, Object... values) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForList().rightPushAll(key, values);
    }

    /**
     * 返回并弹出指定 Key 关联的 Redis 列表中的第一个元素，即头部元素。如果该 Key 不存在，
     * 返回 nil。lLeftPop 命令执行两步操作：第一步是将列表左边的元素从列表中移除，第二步是返回被移除的元素值。
     *
     * @param key 列表 key
     * @return
     */
    public Object lLeftPop(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 返回并弹出指定 Key 关联的 Redis 列表中的最后一个元素，即头部元素
     * 如果该 Key 不存在，返回 nil。
     * lRightPop 命令执行两步操作：第一步是将列表右边的元素从列表中移除，第二步是返回被移除的元素值。
     *
     * @param key 列表 key
     * @return 列表的头部元素
     */
    public Object lRightPop(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 该命令的参数 start 和 end 都是0-based 。即0表示 Redis 列表头部( leftmost )的第一个元素。
     * 其中 start 的值也可以为负值，-1将表示 Redis 列表中的最后一个元素，即尾部元素，-2表示倒数第二个并以此类推。
     * 该命令在获取元素时， start 和 end 位置上的元素也会被取出。如果 start 的值大于 Redis 列表中元素的数量，
     * 空 Redis 列表将会被返回。如果 end 的值大于元素的数量，该命令则获取从 start (包括 start )开始， Redis 列表中剩余的所有元素。
     * 注： Redis 的列表起始索引为0。显然， lRange numbers 0,-1 可以获取列表中的所有元素。返回指定范围内元素的列表。
     *
     * @param key   列表 key
     * @param start 指定区间起始偏移量
     * @param end   指定区间结束偏移量
     * @return 指定范围内元素的 Redis 列表
     */
    public List<Object> lRange(String key, long start, long end) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。
     * 下标 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     *
     * @param key   列表 key
     * @param start 指定区间起始偏移量
     * @param end   指定区间结束偏移量
     */
    public void lTrim(String key, long start, long end) {
        if (null == key) {
            return;
        }
        redisTemplate.opsForList().trim(key, start, end);
    }

    /**
     * 该命令将返回 Redis 列表中指定位置( index )的元素，index 是 0-based，表示从头部位置开始第 index 的元素，
     * 如果index为-1，表示尾部元素。如果与该 Key 关联的不是 Redis 列表，该命令将返回相关的错误信息。 如果超出 index 返回这返回 nil。
     *
     * @param key   列表 key
     * @param index 指定位置
     * @return Redis 列表指定位置元素
     */
    public Object lIndex(String key, long index) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 返回指定Key关联的 Redis 列表中元素的数量，如果该Key不存在，则返回0。
     * 如果与该Key关联的Value的类型不是 Redis 列表，则抛出相关的异常。
     *
     * @param key 列表 key
     * @return 指定Key关联的 Redis 列表中元素的数量
     */
    public Long lSize(String key) {

        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForList().size(key);
    }

    //***************Set数据类型*************

    /**
     * 如果在插入的过程用，参数中有的成员在 Set 中已经存在，该成员将被忽略，而其它成员仍将会被正常插入。
     * 如果执行该命令之前，该 Key 并不存在，该命令将会创建一个新的 Set，此后再将参数中的成员陆续插入。返回实际插入的成员数量。
     *
     * @param key    集合 Key
     * @param values 插入成员
     * @return 实际插入的成员数量
     */
    public Long sAdd(String key, Object... values) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * 返回 Set 中成员的数量，如果该 Key 并不存在，返回0。
     *
     * @param key 集合 Key
     * @return 集合中成员数量
     */
    public Long sSize(String key) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForSet().size(key);

    }

    /**
     * 判断参数中指定成员是否已经存在于与 Key 相关联的 集合中。
     * 返回 true 表示已经存在，false 表示不存在，或该 Key 本身并不存在。
     *
     * @param key 集合 Key
     * @param o   指定成员
     * @return 判断集合中是否存在参数中指定成员
     */
    public Boolean sIsMember(String key, Object o) {
        if (null == key) {
            return false;
        }
        return redisTemplate.opsForSet().isMember(key, o);
    }

    /**
     * 和 sPop 一样，随机的返回 Set 中的一个成员，不同的是该命令并不会删除返回的成员。
     *
     * @param key 集合 Key
     * @return Set 中随机一个成员
     */
    public Object sRandomMember(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 和 sPop 一样，随机的返回 Set 中的一个或多个成员，不同的是该命令并不会删除返回的成员。
     * 还可以传递 count 参数来一次随机获得多个元素，根据 count 的正负不同，具体表现也不同。
     * 当 count  为正数时，sRandomMembers 会随机从集合里获得 count 个不重复的元素。
     * 如果 count 的值大于集合中的元素个数，则 sRandomMembers 会返回集合中的全部元素。
     * 当 count 为负数时，sRandomMembers 会随机从集合里获得|count|个的元素，如果|count|大与集合中的元素，
     * 就会返回全部元素不够的以重复元素补齐，如果key不存在则返回nil。
     *
     * @param key   集合 Key
     * @param count 获取元素个数
     * @return Set 随机的一个或多个成员
     */
    public List<Object> sRandomMembers(String key, int count) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForSet().randomMembers(key, count);
    }

    /**
     * 通过 key 随机删除一个 set 中的 value 并返回该值
     *
     * @param key 集合 Key
     * @return 随机删除值
     */
    public Object sPop(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForSet().pop(key);

    }

    /**
     * 通过 key 获取 set中所有的 value
     *
     * @param key 集合 Key
     * @return set 中所有的 value
     */
    public Set<Object> sMembers(String key) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 从与 Key 关联的 Set 中删除参数中指定的成员，不存在的参数成员将被忽略，
     * 如果该 Key 并不存在，将视为空 Set 处理。返回从 Set 中实际移除的成员数量，如果没有则返回0。
     *
     * @param key    集合 Key
     * @param values 指定成员
     * @return 实际移除的成员数量
     */
    public Long sRemove(String key, Object... values) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 将元素 value 从源集合移到目标集合
     *
     * @param key     集合 Key
     * @param value   集合 Value
     * @param destKey 目标集合 Key
     * @return 集合成员元素是否成功移除
     */
    public Boolean sMove(String key, Object value, String destKey) {
        if (null == key || null == destKey) {
            return false;
        }
        return redisTemplate.opsForSet().move(key, value, destKey);

    }

    /**
     * 获取两个集合并集
     *
     * @param key       集合 Key
     * @param otherKeys 另一 集合 Key
     * @return 两个集合并集
     */
    public Set<Object> sUnion(String key, String otherKeys) {
        if (null == key || otherKeys == null) {
            return null;
        }
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    //**********Sorted Set 数据类型********************

    /**
     * 添加参数中指定的所有成员及其分数到指定 key 的 Sorted Set 中，在该命令中我们可以指定多组 score/value 作为参数。
     * 如果在添加时参数中的某一成员已经存在，该命令将更新此成员的分数为新值，同时再将该成员基于新值重新排序。
     * 如果键不存在，该命令将为该键创建一个新的 Sorted Set Value，并将 score/value 对插入其中。
     *
     * @param key   有序集 Key
     * @param value 有序集 Value
     * @param score 分数
     * @return 有序集基数
     */
    public Boolean zAdd(String key, Object value, double score) {
        if (null == key) {
            return false;
        }
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 该命令将移除参数中指定的成员，其中不存在的成员将被忽略。
     * 如果与该 Key 关联的 Value 不是 Sorted Set，相应的错误信息将被返回。
     * 如果操作成功则返回实际被删除的成员数量。
     *
     * @param key    有序集 Key
     * @param values 有序集 Value
     * @return 实际被删除的成员数量
     */
    public Long zRemove(String key, Object... values) {
        if (null == key || null == values) {
            return 0L;
        }
        return redisTemplate.opsForZSet().remove(key, values);
    }

    /**
     * 获取有序集成员数量
     * 如果该 Key 不存在，返回 0
     *
     * @param key 有序集 Key
     * @return 有序集成员数量
     */
    public Long zSize(String key) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 该命令将为指定 Key 中的指定成员增加指定的分数。如果成员不存在，该命令将添加该成员并假设其初始分数为0，
     * 此后再将其分数加上 increment 。如果 Key 不存在，该命令将创建该 Key 及其关联的 Sorted Set ，
     * 并包含参数指定的成员，其分数为 increment 参数。如果与该 Key 关联的不是 Sorted Set 类型，
     * 相关的错误信息将被返回。如果不报错则以串形式表示的新分数。
     *
     * @param key   有序集 Key
     * @param value 有序集 Value
     * @param score 分数
     * @return 指定成员的新分数值
     */
    public Double zIncrementScore(String key, Object value, double score) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().incrementScore(key, value, score);
    }

    /**
     * 该命令用于获取分数( score )在 min 和 max 之间的成员数量。
     * （ min =< score <= max ）如果加上了“(”着表明是开区间，例如 zCount key (min max 则 表示（ min < score =< max）
     * 同理 zCount key min ( max 则表明( min =< score < max) 返回指定返回数量。
     *
     * @param key 有序集 Key
     * @param min 分数下限
     * @param max 分数上限
     * @return 分数值在 min 和 max 之间的成员的数量
     */
    public Long zCount(String key, double min, double max) {
        if (null == key) {
            return 0L;
        }
        return redisTemplate.opsForZSet().count(key, min, max);
    }

    /**
     * Sorted Set中的成员都是按照分数从低到高的顺序存储，该命令将返回参数中指定成员的位置值，
     * 其中0表示第一个成员，它是 Sorted Set 中分数最低的成员。 如果该成员存在，则返回它的位置索引值。否则返回 nil。
     *
     * @param key 有序集 Key
     * @param o   指定成员
     * @return 有序集中指定成员的排名
     */
    public Long zRank(String key, Object o) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().rank(key, o);

    }

    /**
     * 如果该成员存在，以字符串的形式返回其分数，否则返回null
     *
     * @param key 有序集 Key
     * @param o   指定成员
     * @return 有序集中指定成员分数
     */
    public Double zScore(String key, Object o) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().score(key, o);
    }

    /**
     * 该命令返回顺序在参数 start 和 stop 指定范围内的成员，这里 start 和 stop 参数都是0- based ，即0表示第一个成员，-1表示最后一个成员。如果 start 大于该 Sorted
     * Set 中的最大索引值，或 start > stop ，此时一个空集合将被返回。如果 stop 大于最大索引值，
     * 该命令将返回从 start 到集合的最后一个成员。如果命令中带有可选参数 WITHSCORES 选项，
     * 该命令在返回的结果中将包含每个成员的分数值，如 value 1, score 1, value 2, score 2...。
     *
     * @param key   有序集 Key
     * @param start 指定排名区间开始
     * @param end   指定排名区间结束
     * @return 顺序在参数 start 和 stop 指定范围内的有序集成员
     */
    public Set<Object> zRange(String key, long start, long end) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().range(key, start, end);

    }

    /**
     * 该命令的功能和 ZRange 基本相同，唯一的差别在于该命令是通过反向排序获取指定位置的成员，
     * 即从高到低的顺序。如果成员具有相同的分数，则按降序字典顺序排序。
     *
     * @param key   有序集 Key
     * @param start 指定排名区间开始
     * @param end   指定排名区间结束
     * @return 反向顺序在参数 start 和 stop 指定范围内的有序集成员
     */
    public Set<Object> zReverseRange(String key, long start, long end) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().reverseRange(key, start, end);

    }

    /**
     * 该命令将返回分数在 min 和 max 之间的所有成员，即满足表达式 min <= score <= max 的成员，
     * 其中返回的成员是按照其分数从低到高的顺序返回，如果成员具有相同的分数，
     * 则按成员的字典顺序返回。可选参数 LIMIT 用于限制返回成员的数量范围。
     * 可选参数 offset 表示从符合条件的第 offset 个成员开始返回，同时返回 count 个成员。
     * 可选参数 WITHSCORES 的含义参照 ZRANGE 中该选项的说明。*最后需要说明的是参数中 min 和 max 的规则可参照命令 ZCOUNT 。
     *
     * @param key 有序集 Key
     * @param min 分数下限
     * @param max 分数上限
     * @return 分数在 min 和 max 之间的所有成员
     */
    public Set<Object> zRangeByScore(String key, double min, double max) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * 该命令除了排序方式是基于从高到低的分数排序之外，其它功能和参数含义均与 zRangeByScore 相同。
     * 需要注意的是该命令中的min和max参数的顺序和 zRangeByScore 命令是相反的。
     *
     * @param key 有序集 Key
     * @param min 分数下限
     * @param max 分数上限
     * @return 反向顺序分数在 min 和 max 之间的所有成员
     */
    public Set<Object> zReverseRangeByScore(String key, double min, double max) {
        if (null == key) {
            return null;
        }
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }
}